package util.util;

import com.fasterxml.jackson.databind.*;
import util.model.common.FieldConfig;
import util.model.common.FieldInfo;
import util.util.annotation.ConvertField;
import util.util.error.BizException;
import util.util.error.JsonReturnCode;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;

public class DevBeanUtil {


  /**
   * FieldInfo 集合转换
   *
   * @param list 读入集合
   * @param <T>  读入的实体
   * @return FieldInfo集合
   */
  public static <T> List<FieldInfo> fieldInfoList(List<T> list) {
    List<FieldInfo> fieldInfoList = new ArrayList<>(list.size());
    for (T t : list) {
      fieldInfoList.add(fieldInfo(t));
    }
    return fieldInfoList;
  }

  /**
   * FieldInfo 实体转换
   *
   * @param value 读入实体
   * @param <T>  读入的实体
   * @return FieldInfo
   */
  public static <T> FieldInfo fieldInfo(T value) {
    FieldInfo fieldInfo = new FieldInfo();
    copy(value, fieldInfo);
    return fieldInfo;
  }

  /**
   * 插入对象的某属性值
   *
   * @param object     对象
   * @param fieldName  属性名称
   * @param fieldValue 属性值
   * @return 属性值
   */
  public static Object setObjectValue(Object object, String fieldName, Object fieldValue) {
    Class tClass = object.getClass();
    try {
      Field field = tClass.getDeclaredField(fieldName);
      field.setAccessible(true);
      field.set(object, fieldValue);
    } catch (NoSuchFieldException e) {
      e.printStackTrace();
      throw new BizException(JsonReturnCode.BIZ_NO_FIELD.addParam("msg", fieldName));
    } catch (IllegalAccessException e) {
      e.printStackTrace();
      throw new BizException(JsonReturnCode.BIZ_ILLEGAL_ACCESS_ERROR);
    }
    return object;
  }

  /**
   * 某对象为空，则赋值。
   *
   * @param object     对象
   * @param fieldName  属性名
   * @param fieldValue 值
   * @return
   */
  public static Object setNullObjectValue(Object object, String fieldName, Object fieldValue) {

    Class tClass = object.getClass();
    try {
      Field[] fields = tClass.getDeclaredFields();
      for (Field field : fields) {
        if (field.getName().equals(fieldName)) {
          field.setAccessible(true);
          field.set(object, fieldValue);
        }
      }
      return object;
    } catch (IllegalAccessException e) {
      e.printStackTrace();
      throw new BizException(JsonReturnCode.BIZ_ILLEGAL_ACCESS_ERROR);
    }
  }


  /**
   * 获取对象的某属性值
   *
   * @param object    对象
   * @param fieldName 属性名称
   * @return 属性值
   */
  public static Object getObjectValue(Object object, String fieldName) {
    Class tClass = object.getClass();
    Field[] fields = tClass.getDeclaredFields();
    for (Field fd : fields) {
      if (fd.getName().equals(fieldName)) {
        fd.setAccessible(true);
        try {
          return fd.get(object);
        } catch (IllegalAccessException e) {
          throw new BizException(JsonReturnCode.FIELD_NOT_FOUND_ERROR);
        }
      }
    }
    return null;
  }

  /**
   * bean->map（bean中的Date改为String,yyyy-MM-dd）
   *
   * @param object 对象
   * @return 属性值
   */
  public static Map bean2Map(Object object) {
    if (object instanceof Map) {
      return (Map) object;
    }
    Map<String, Object> map = new HashMap();
    Class tClass = object.getClass();
    Field[] fields = tClass.getDeclaredFields();
    for (Field field : fields) {
      field.setAccessible(true);
      String name = field.getName();
      try {
        Object value = field.get(object);
        Class<?> type = field.getType();
        if (type == Date.class) {
          value = DevDateUtil.date2Str((Date) value);
        }
        map.put(name, value);
      } catch (IllegalAccessException e) {
        e.printStackTrace();
        
        throw new BizException(JsonReturnCode.BIZ_ILLEGAL_ACCESS_ERROR);
      }
    }
    return map;
  }


  /**
   * bean->map(bean中类型是不改变)
   *
   * @param object 对象
   * @return 属性值
   */
  public static Map bean2MapPrototype(Object object) {
    if (object instanceof Map) {
      return (Map) object;
    }
    Map<String, Object> map = new HashMap();
    Class tClass = object.getClass();
    Field[] fields = tClass.getDeclaredFields();
    for (Field field : fields) {
      field.setAccessible(true);
      String name = field.getName();
      try {
        Object value = field.get(object);
        map.put(name, value);
      } catch (IllegalAccessException e) {
        e.printStackTrace();
        
        throw new BizException(JsonReturnCode.BIZ_ILLEGAL_ACCESS_ERROR);
      }
    }
    return map;
  }


  /**
   * map->bean
   *
   * @param tClass 转换的对象类型
   * @param map    要转换的Map
   * @param <T>    生成的对象
   * @return
   */
  public static <T> T map2Bean2(Class<T> tClass, Map<String, Object> map) {
    T obj = null;
    try {

      if (!DevComUtil.isEmpty(map)) {
        obj = tClass.newInstance();
        Field[] fields = tClass.getDeclaredFields();
        for (Field field : fields) {
          Class type = field.getType();
          String fieldValue = null;
          String fieldName = field.getName();
          if (map.containsKey(fieldName)) {
            Object object = map.get(fieldName);
            if (!DevComUtil.isEmpty(object)) {
              fieldValue = object.toString();
            }
          }
          //转换为需要的类型
          Object value = parseValue(fieldValue, type);
          //赋值
          field.setAccessible(true);
          field.set(obj, value);
        }
      }
    } catch (IllegalAccessException e) {
      
      throw new BizException(JsonReturnCode.BIZ_NO_CLASS_PATH_ERROR);
    } catch (InstantiationException e) {
      
      throw new BizException(JsonReturnCode.BIZ_NO_CLASS_PATH_ERROR);
    }

    return obj;

  }

  /**
   * 复制对象
   *
   * @param froObj 原对象
   * @param toObj  赋值之后的对象
   * @return
   */
  public static Object copy(Object froObj, Object toObj) {
//    try {
////      BeanUtils.copyProperties(froObj, toObj);
//    } catch (IllegalAccessException e) {
//      e.printStackTrace();
//    } catch (InvocationTargetException e) {
//      e.printStackTrace();
//    }
    return toObj;
  }

  /**
   * 复制对象
   *
   * @param list 原对象
   * @param claz  赋值之后的对象
   * @return
   */
  public static <T, V> List<V> copyList(List<T> list, Class<V> claz) {
    List<V> toList = new ArrayList<>(list.size());
//    for (T t : list) {
//      V v = newInstance(claz);
//      try {
//        BeanUtils.copyProperties(t, v);
//      } catch (IllegalAccessException e) {
//        e.printStackTrace();
//      } catch (InvocationTargetException e) {
//        e.printStackTrace();
//      }
//      toList.add(v);
//    }

    return toList;
  }

  /**
   * 对象转换
   *
   * @param obj 原对象
   * @param clz 需要转换成的对象
   * @param <T> 转换之后的对象
   * @return
   * @throws Exception
   */
  public static <T> T entityPropertyMap(Object obj, Class<T> clz) {
    ObjectMapper om = new ObjectMapper();
    om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    om.setDateFormat(new SimpleDateFormat(DevDateUtil.DATE_YMD_));
    /*  om.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);*/
    T res;
    try {
      String tmp = om.writeValueAsString(obj);
      res = om.readValue(tmp, clz);
    } catch (IOException e) {
      e.printStackTrace();
      
      throw new BizException(JsonReturnCode.FIELD_TO_FIELD_ERROR);
    }
    return res;
  }


  /**
   * 转换类型，把实体根据fieldMap中对应的字段 转化为claz。
   *
   * @param object   原实体数据
   * @param claz     新实体
   * @param fieldMap 转换字段配置
   * @param <T>      新实体
   * @return 新实体
   */
  public static <T> T beanCovert(Object object, Class<T> claz, Map<String, String> fieldMap) {
    if (DevComUtil.isEmpty(claz)) {
      throw new BizException(JsonReturnCode.BIZ_NULL_CLASS_PARAM.addParam("msg", claz.getName()));
    }
    T t;
    try {
      t = claz.newInstance();
    } catch (InstantiationException e) {
      e.printStackTrace();
      throw new BizException(JsonReturnCode.BIZ_NO_INST_PARAM.addParam("msg", claz.getName()));
    } catch (IllegalAccessException e) {
      e.printStackTrace();
      throw new BizException(JsonReturnCode.BIZ_ILLEGAL_ACCESS_ERROR_PARAM.addParam("msg", claz.getName()));
    }
    if (!DevComUtil.isEmpty(fieldMap)) {
      Set<Map.Entry<String, String>> entries = fieldMap.entrySet();
      for (Map.Entry<String, String> entry : entries) {
        Object objectValue = DevBeanUtil.getObjectValue(object, entry.getValue());
        DevBeanUtil.setObjectValue(t, entry.getKey(), objectValue);
      }
    }
    return t;
  }

  /**
   * 转换类型，把实体根据fieldMap中对应的字段 转化为claz。
   *
   * @param object      原实体数据
   * @param claz        新实体
   * @param fieldConfig 转换字段配置
   * @param <T>         新实体
   * @return 新实体
   */
  public static <T> T beanCovert(Object object, Class<T> claz, FieldConfig fieldConfig) {
    T t = newInstance(claz);
    return beanCovert(object, t, fieldConfig);
  }

  /**
   * 实例化实体
   *
   * @param claz 新实体
   * @param <T>  新实体
   * @return 新实体
   */
  public static <T> T newInstance(Class<T> claz) {
    if (DevComUtil.isEmpty(claz)) {
      throw new BizException(JsonReturnCode.BIZ_NULL_CLASS_PARAM.addParam("msg", ""));
    }
    T t;
    try {
      t = claz.newInstance();
    } catch (InstantiationException e) {
      e.printStackTrace();
      throw new BizException(JsonReturnCode.BIZ_NO_INST_PARAM.addParam("msg", claz.getName()));
    } catch (IllegalAccessException e) {
      e.printStackTrace();
      throw new BizException(JsonReturnCode.BIZ_ILLEGAL_ACCESS_ERROR_PARAM.addParam("msg", claz.getName()));
    }
    return t;
  }

  /**
   * 转换类型，把实体根据fieldMap中对应的字段 转化为claz。
   *
   * @param object      原实体数据
   * @param t           新实体
   * @param fieldConfig 转换字段配置
   * @param <T>         新实体
   * @return 新实体
   */
  public static <T> T beanCovert(Object object, T t, FieldConfig fieldConfig) {
    if (DevComUtil.isEmpty(t)) {
      throw new BizException(JsonReturnCode.BIZ_NULL_CLASS_PARAM.addParam("msg", ""));
    }

    if (!DevComUtil.isEmpty(fieldConfig)) {
      List<FieldInfo> fieldList = fieldConfig.getFieldList();

      Field[] newFields = t.getClass().getDeclaredFields();

      Field[] fields = object.getClass().getDeclaredFields();

      for (FieldInfo fieldModel : fieldList) {
        Optional<Field> fieldOptional = Arrays.stream(fields).filter(item -> fieldModel.getFieldName().equals(item.getName())).findFirst();
        if (!fieldOptional.isPresent()) {
          throw new BizException(JsonReturnCode.BIZ_NO_FIELD.addParam("msg", fieldModel.getFieldName()));
        }
        Field field = fieldOptional.get();

        Optional<Field> newFieldOptional = Arrays.stream(newFields).filter(item -> fieldModel.getFieldNewName().equals(item.getName())).findFirst();
        if (!newFieldOptional.isPresent()) {
          throw new BizException(JsonReturnCode.BIZ_NO_FIELD.addParam("msg", fieldModel.getFieldNewName()));
        }
        Field newField = newFieldOptional.get();

        Class<?> newType = newField.getType();
        Object obj;
        try {
          field.setAccessible(true);
          obj = field.get(object);

          if (obj != null) {
            newField.setAccessible(true);
            if (!field.getType().equals(newType)) {
              if (BigDecimal.class.equals(newType)) {
                BigDecimal value = DevNumUtil.strToDec(obj.toString());
                String fieldScale = fieldModel.getFieldScale();
                if (!"0".equals(fieldScale)) {
                  int scale = Integer.parseInt(fieldScale);
                  value = value.setScale(scale, BigDecimal.ROUND_HALF_UP);
                }
                newField.set(t, value);
                continue;
              } else if (Date.class.equals(newType)) {
                String formatStr = fieldModel.getFormatStr();
                if (formatStr == null) {
                  formatStr = DevDateUtil.DATE_YMD_;
                }
                Date value = DevDateUtil.str2Date(formatStr, obj.toString());
                newField.set(t, value);
                continue;
              } else {
                throw new BizException("请配置" + newType.getName());
              }
            }
            newField.set(t, obj);
          }
        } catch (IllegalAccessException e) {
          e.printStackTrace();
          throw new BizException(JsonReturnCode.BIZ_ILLEGAL_ACCESS_ERROR_PARAM.addParam("msg", newField.getName()));
        }

      }

    }
    return t;
  }


  /**
   * 转换类型，把实体根据fieldMap中对应的字段 转化为claz。
   *
   * @param map         原实体数据
   * @param t           新实体
   * @param fieldConfig 转换字段配置
   * @param <T>         新实体
   * @return 新实体
   */
  public static <T> T mapCovert(Map<String, String> map, T t, FieldConfig fieldConfig) {
    if (DevComUtil.isEmpty(t)) {
      throw new BizException(JsonReturnCode.BIZ_NULL_CLASS_PARAM.addParam("msg", ""));
    }
    if (!DevComUtil.isEmpty(fieldConfig)) {
      List<FieldInfo> fieldList = fieldConfig.getFieldList();
      Field[] newFields = t.getClass().getDeclaredFields();
      for (FieldInfo fieldModel : fieldList) {
        String valueStr = map.get(fieldModel.getFieldName());
        Optional<Field> newFieldOptional = Arrays.stream(newFields).filter(item -> fieldModel.getFieldNewName().equals(item.getName())).findFirst();
        if (!newFieldOptional.isPresent()) {
          throw new BizException(JsonReturnCode.BIZ_NO_FIELD.addParam("msg", fieldModel.getFieldNewName()));
        }
        Field newField = newFieldOptional.get();
        Class<?> newType = newField.getType();
        try {
          if (valueStr != null && !"".equals(valueStr)) {
            newField.setAccessible(true);
            if (!String.class.equals(newType)) {
              if (BigDecimal.class.equals(newType)) {
                BigDecimal value = DevNumUtil.strToDec(valueStr);
                String fieldScale = fieldModel.getFieldScale();
                if (!"0".equals(fieldScale)) {
                  int scale = Integer.parseInt(fieldScale);
                  value = value.setScale(scale, BigDecimal.ROUND_HALF_UP);
                }
                newField.set(t, value);
                continue;
              } else if (Date.class.equals(newType)) {
                String formatStr = fieldModel.getFormatStr();
                if (formatStr == null) {
                  formatStr = DevDateUtil.DATE_YMD_;
                }
                Date value = DevDateUtil.str2Date(formatStr, valueStr);
                newField.set(t, value);
                continue;
              } else if (Integer.class.equals(newType)) {
                newField.set(t, Integer.parseInt(valueStr));
                continue;
              } else if (Short.class.equals(newType)) {
                newField.set(t, Short.parseShort(valueStr));
                continue;
              } else if (Long.class.equals(newType)) {
                newField.set(t, Long.parseLong(valueStr));
                continue;
              } else if (LocalDateTime.class.equals(newType)) {
                newField.set(t,LocalDateTime.parse(valueStr));
                continue;
              } else if (LocalTime.class.equals(newType)) {
                newField.set(t,LocalTime.parse(valueStr));
                continue;
              }else {
                throw new BizException("请配置" + newType.getName());
              }
            }
            newField.set(t, valueStr);
          }
        } catch (IllegalAccessException e) {
          e.printStackTrace();
          throw new BizException(JsonReturnCode.BIZ_ILLEGAL_ACCESS_ERROR_PARAM.addParam("msg", newField.getName()));
        }
      }
    }
    return t;
  }


  /**
   * 转换类型，把实体根据fieldMap中对应的字段 转化为claz。
   *
   * @param map         原实体数据
   * @param claz        新实体
   * @param fieldConfig 转换字段配置
   * @param <T>         新实体
   * @return 新实体
   */
  public static <T> T mapCovert(Map<String, String> map, Class<T> claz, FieldConfig fieldConfig) {
    T t = newInstance(claz);
    return mapCovert(map, t, fieldConfig);
  }

  /**
   * 转换值
   *
   * @param fieldValue
   * @param tClass
   * @return
   */
  public static Object parseValue(String fieldValue, Class tClass) {
    Object value = null;
    if (!DevComUtil.isEmpty(fieldValue)) {
      if (tClass == BigDecimal.class) {
        value = new BigDecimal(fieldValue);
      } else if (tClass == Date.class) {
        value = DevDateUtil.str2Date(fieldValue);
      } else if (tClass == Integer.class) {
        value = Integer.parseInt(fieldValue);
      } else if (tClass == Short.class) {
        value = Short.parseShort(fieldValue);
      } else if (tClass == Long.class) {
        value = Long.parseLong(fieldValue);
      } else if (tClass == Double.class) {
        value = Double.parseDouble(fieldValue);
      } else if (tClass == Float.class) {
        value = Float.parseFloat(fieldValue);
      } else {
        value = fieldValue;
      }
    }
    return value;
  }


  /**
   * 获取转换的实体注解配置
   *
   * @param tClaz 需要转换的实体
   * @return fieldConfig
   */
  public static FieldConfig getFieldConfig(Class<?> tClaz) {
    Field[] declaredFields = tClaz.getDeclaredFields();
    FieldConfig fieldConfig = new FieldConfig(declaredFields.length);
    String name;
    String newName;
    String dateFormat;
    int fieldlength;
    int fieldScale;
    ConvertField annotation;
    for (Field declaredField : declaredFields) {
      annotation = declaredField.getAnnotation(ConvertField.class);
      if (annotation != null) {
        name = declaredField.getName();
        newName = annotation.newName();
        if (DevComUtil.isEmpty(newName)) {
          newName = name;
        }
        dateFormat = annotation.dateFormat();
        fieldlength = annotation.fieldLength();
        fieldScale = annotation.fieldScale();
        fieldConfig.beanConvert(name, newName, String.valueOf(fieldlength), String.valueOf(fieldScale), dateFormat);
      }
    }
    return fieldConfig;
  }

  /**
   * 字符串转换为List<T>
   *
   * @param str    需要转换的字符串
   * @param tClass 实体
   * @param <T>    实体类型
   * @return 转换的集合
   */
  public static <T> List<T> str2BeanList(String str, Class<T> tClass) {
    if (DevComUtil.isEmpty(str)) {
      return null;
    }
    try {
      ObjectMapper objectMapper = getMapper();
      JavaType javaType = objectMapper.getTypeFactory().constructParametricType(List.class, tClass);
      return objectMapper.readValue(str, javaType);
    } catch (IOException e) {
      e.printStackTrace();

      throw new BizException(JsonReturnCode.FIELD_TO_FIELD_ERROR_PARAM.addParam("msg", str));
    }
  }

  /**
   * 将XML转为指定的bean
   *
   * @param clazz
   * @param xmlStr
   * @return
   * @throws Exception
   */
  public static <T> T xml2Bean(Class<T> clazz, String xmlStr) {
    T obj = null;
    if (DevComUtil.isEmpty(clazz) || DevComUtil.isEmpty(xmlStr)) {
      return obj;
    } else {
      xmlStr = DevComUtil.trimStr(xmlStr);
      try (Reader reader = new StringReader(xmlStr)) {
        JAXBContext context = JAXBContext.newInstance(clazz);
        // XML 转为对象的接口
        Unmarshaller unmarshaller = context.createUnmarshaller();
        obj = (T) unmarshaller.unmarshal(reader);
      } catch (JAXBException e) {
        e.printStackTrace();
        throw new BizException(JsonReturnCode.STRING_TO_DOCUMENT_ERROR_PARAM.addParam("msg", xmlStr));
      } catch (IOException e) {
        e.printStackTrace();
        throw new BizException(JsonReturnCode.IO_ERROR);
      }
    }
    return obj;
  }


  public static ObjectMapper getMapper() {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd", Locale.CHINESE));
    objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);//框架默认的是大小写不敏感的
    // 忽略json字符串中不识别的属性
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    // 忽略无法转换的对象 “No serializer found for class com.xxx.xxx”
    objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
    return objectMapper;
  }

}
